#include "ap3216c.h"

//混杂设备对象
static struct miscdevice ap3216c_misc;
static struct i2c_client *ap3216c_client;

static int ap3216c_read_regs(struct i2c_client *client, uint8_t reg, uint8_t *data, uint8_t lenght)
{
	struct i2c_msg msg[2];

	//从机地址
	msg[0].addr = client->addr;
	//表示写
	msg[0].flags = 0;
	//buf是一个指针,指向了要发送的数据
	msg[0].buf = &reg;
	//msg[0].buf的数据长度
	msg[0].len = 1;

	msg[1].addr = client->addr;
	//表示读
	msg[1].flags = I2C_M_RD;
	msg[1].buf = data;
	msg[1].len = lenght;
	if(i2c_transfer(client->adapter, msg, 2)  == 2)
		return 0;
	else 
		return -EIO;
}

static int ap3216c_write_regs(struct i2c_client *client, uint8_t reg, uint8_t *data, uint8_t lenght)
{
	uint8_t buffer[256];
	struct i2c_msg msg[1];
	
	//只能用一个msg发送，分多个msg时msg衔接的时候不会等待设备的ACK信号，可能会导致失败
	buffer[0] = reg;
	memcpy(&buffer[1], data, lenght);
	//从机地址
	msg[0].addr = client->addr;
	//表示写
	msg[0].flags = 0;
	//buf是一个指针,指向了要发送的数据
	msg[0].buf = buffer;
	//msg[0].buf的数据长度
	msg[0].len = 1 + lenght;
	if(i2c_transfer(client->adapter, msg, 1)  == 1)
		return 0;
	else 
		return -EIO;
}

static int ap3216c_read_reg(struct i2c_client *client, uint8_t reg, uint8_t *data)
{
	return ap3216c_read_regs(client, reg, data, 1);
}

static int ap3216c_write_reg(struct i2c_client *client, uint8_t reg, uint8_t data)
{
	return ap3216c_write_regs(client, reg, &data, 1);
}

static int ap3216c_open(struct inode *inode, struct file *filp)
{
	int result;

	//初始化AP3216C，然后开启ALS、PS+IR
	result = ap3216c_write_reg(ap3216c_client, AP3216C_SYSTEMCONG, 0x04);
	if(result != 0)
		return result;
	mdelay(50);	
	result = ap3216c_write_reg(ap3216c_client, AP3216C_SYSTEMCONG, 0X03);
	if(result != 0)
		return result;

	return 0;
}

static int ap3216c_release(struct inode *inode, struct file *filp)
{
	return 0;
}

static ssize_t ap3216c_read(struct file *filp, char __user *user_buf, size_t cnt, loff_t *off)
{
	int i;
	int result;
	uint16_t data[3];
	uint8_t buffer[6];
	
	/* 循环读取所有传感器数据 */
	for(i=0; i<6; i++) 
	{
		result = ap3216c_read_reg(ap3216c_client, AP3216C_IRDATALOW+i, &buffer[i]);
		if(result != 0)
			return result;
	}

	if(buffer[0] & 0X80) /* IR_OF位为1,则数据无效 */
		data[0] = 0;
	else /* 读取IR传感器的数据*/
		data[0] = ((uint16_t)buffer[1] << 2) | (buffer[0] & 0X03);
	
	data[1] = ((uint16_t)buffer[3] << 8) | buffer[2]; /* 读取ALS传感器的数据 */
	
	if(buffer[4] & 0x40) /* IR_OF位为1,则数据无效 */
		data[2] = 0;
	else /* 读取PS传感器的数据 */
		data[2] = ((uint16_t)(buffer[5] & 0X3F) << 4) | (buffer[4] & 0X0F);

	result = copy_to_user(user_buf, data, sizeof(data));
	if(result != 0)
		return -EFAULT;

	return 6;
}

static struct file_operations ap3216c_ops = {
	.owner = THIS_MODULE,
	.open = ap3216c_open,
	.read = ap3216c_read,
	.release = ap3216c_release,
};
static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	printk("%s\r\n", __FUNCTION__);
	//暂存客户端
	ap3216c_client = client;
	//注册混杂设备
	ap3216c_misc.minor = AP3216C_MINOR;
	ap3216c_misc.name = "ap3216c";
	ap3216c_misc.fops = &ap3216c_ops;
	return misc_register(&ap3216c_misc);
}

static int ap3216c_remove(struct i2c_client *client)
{
	printk("%s\r\n", __FUNCTION__);
	//注销混杂设备
	misc_deregister(&ap3216c_misc);
	return 0;
}

static const struct i2c_device_id ap3216c_id[] = {
	//采用设备树匹配，这里可以为空
//	{"alientek,ap3216c", 0},
	{ /* Sentinel */ }
};

static const struct of_device_id ap3216c_of_match[] = {
	{ .compatible = "alientek,ap3216c" },
	{ /* Sentinel */ }
};

static struct i2c_driver ap3216c_driver = {
	.probe = ap3216c_probe,
	.remove = ap3216c_remove,
	.driver = {
			.owner = THIS_MODULE,
			.name = "ap3216c",
			.of_match_table = ap3216c_of_match, 
		},
	//I2C驱动的此项参数不能为NULL
	.id_table = ap3216c_id, 
};

static int __init ap3216c_init(void)
{
	int result;

	printk("%s\r\n", __FUNCTION__);
	result = i2c_add_driver(&ap3216c_driver);
	if(result != 0)
	{
		printk("add ap3216c driver failed\r\n");
		return result;
	}
	return 0;
}

static void __exit ap3216c_exit(void)
{
	printk("%s\r\n", __FUNCTION__);
	i2c_del_driver(&ap3216c_driver);
}

module_init(ap3216c_init);
module_exit(ap3216c_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("lf");
MODULE_DESCRIPTION("ap3216c driver");
